package com.wctj.api.infra.cmb;

import org.apache.ibatis.logging.Log;
import org.apache.ibatis.logging.LogFactory;
import org.bouncycastle.asn1.ASN1EncodableVector;
import org.bouncycastle.asn1.ASN1Integer;
import org.bouncycastle.asn1.DERSequence;
import org.bouncycastle.crypto.params.ECDomainParameters;
import org.bouncycastle.crypto.params.ECPublicKeyParameters;
import org.bouncycastle.crypto.params.ParametersWithID;
import org.bouncycastle.crypto.signers.SM2Signer;
import org.bouncycastle.jce.ECNamedCurveTable;
import org.bouncycastle.jce.spec.ECParameterSpec;
import org.bouncycastle.math.ec.ECCurve;
import org.bouncycastle.math.ec.ECPoint;
import org.bouncycastle.util.encoders.Base64;

import java.math.BigInteger;


public class VerifySigDemo {
    private VerifySigDemo() {
    }
    private static final Log logger = LogFactory.getLog(VerifySigDemo.class);

    public static void main(String[] args) {
        String resSignSource="{\"sigtim\":\"20220325173400\",\"sigdat\":\"__signature_sigdat__\",\"notdat\":\"{\\\"bnkFlg\\\":\\\"Y\\\",\\\"ccyNbr\\\":\\\"10\\\",\\\"crtAcc\\\":\\\"755916095410104\\\",\\\"crtBnk\\\":\\\"招商银行\\\",\\\"dbtAcc\\\":\\\"755916095410100\\\",\\\"dbtBbk\\\":\\\"43\\\",\\\"dbtNam\\\":\\\"422zt\\\",\\\"eptDat\\\":\\\"20220325\\\",\\\"eptTim\\\":\\\"0\\\",\\\"nusAge\\\":\\\"用途\\\",\\\"oprDat\\\":\\\"20220325\\\",\\\"reqNbr\\\":\\\"8720019233\\\",\\\"reqSts\\\":\\\"FIN\\\",\\\"rtnFlg\\\":\\\"S\\\",\\\"stlChn\\\":\\\"Q\\\",\\\"trsAmt\\\":1.03,\\\"trxSet\\\":\\\"K26913V901AAABJ\\\",\\\"yurRef\\\":\\\"202008260078000088\\\"}\",\"notkey\":\"755916095410100\",\"usrnbr\":\"N002432758\",\"notnbr\":\"1507289463178076163\",\"nottyp\":\"YQN02030\"}";
        String resSign="SPu3ZEwzGd37xVZyRZIm+XUGIZGwbGlxEY1byRyYk542ry1fCxb/y4JdneIbApunpoUshVIU0P7PbuTqDpbf+Q==";
        String bankPubkey="BNRhE10qHce4PRt8hCxAPfTmMDxW0Htw9SZHoUWn7U0Qj4GbU2Tgic4EmQSFjTcTdbDvNVmoSzwQvUkfzpRC9+k=";

        cmbSM2Verify(bankPubkey, resSignSource, resSign);
    }

    /**
     * 使用SM2公钥进行验证签名
     * @return
     */
    public static boolean cmbSM2Verify(String sPubKey, String strToSign, String strSign){
        byte[] USER_ID = "1234567812345678".getBytes();
        try {
            byte[] pubKeys = Base64.decode(sPubKey);
            byte[] byteBuffer = strToSign.getBytes("UTF-8");
            byte[] signature = Base64.decode(strSign);
            ECPublicKeyParameters publicKey = encodePublicKey(pubKeys);
            SM2Signer signer = new SM2Signer();
            ParametersWithID parameters = new ParametersWithID(publicKey, USER_ID);
            signer.init(false, parameters);
            signer.update(byteBuffer, 0, byteBuffer.length);
            return signer.verifySignature(encodeDERSignature(signature));
            /*if (!signer.verifySignature(encodeDERSignature(signature))) {
                throw new Exception("请求签名校验不通过");
            }*/
        } catch (Exception e) {
            logger.error(e.getMessage());
        }
        return false;
    }


    private static byte[] encodeDERSignature(byte[] signature) throws Exception {
        byte[] r = new byte[32];
        byte[] s = new byte[32];
        System.arraycopy(signature, 0, r, 0, 32);
        System.arraycopy(signature, 32, s, 0, 32);
        ASN1EncodableVector vector = new ASN1EncodableVector();
        vector.add(new ASN1Integer(new BigInteger(1, r)));
        vector.add(new ASN1Integer(new BigInteger(1, s)));


        try {
            return (new DERSequence(vector)).getEncoded();
        } catch (Exception e) {
            throw new Exception("签名数据不正常");
        }
    }
    private static ECPublicKeyParameters encodePublicKey(byte[] value) {
        byte[] x = new byte[32];
        byte[] y = new byte[32];
        System.arraycopy(value, 1, x, 0, 32);
        System.arraycopy(value, 33, y, 0, 32);
        BigInteger iX = new BigInteger(1, x);
        BigInteger iY = new BigInteger(1, y);
        ECPoint ecQ = getSM2Curve().createPoint(iX, iY);
        return new ECPublicKeyParameters(ecQ, getECDomainParameters());
    }


    private static ECCurve getSM2Curve() {
        ECParameterSpec spec = ECNamedCurveTable.getParameterSpec("sm2p256v1");
        return spec.getCurve();
    }
    private static ECDomainParameters getECDomainParameters() {
        ECParameterSpec spec = ECNamedCurveTable.getParameterSpec("sm2p256v1");
        return new ECDomainParameters(spec.getCurve(), spec.getG(), spec.getN(), spec.getH(), spec.getSeed());
    }
}